package top.wilsonlv.jaguar.cloud.auth.config;

import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
import top.wilsonlv.jaguar.captcha.filter.JaguarCaptchaFilter;
import top.wilsonlv.jaguar.cloud.auth.extension.ExtensionSecurityConfigurer;
import top.wilsonlv.jaguar.cloud.auth.extension.sms.SmsApi;
import top.wilsonlv.jaguar.cloud.auth.filter.LogoutLogFilter;
import top.wilsonlv.jaguar.cloud.auth.security.LoginFailureHandler;
import top.wilsonlv.jaguar.cloud.auth.security.LoginSuccessHandler;
import top.wilsonlv.jaguar.cloud.auth.security.LogoutSuccessHandler;
import top.wilsonlv.jaguar.cloud.auth.security.UserDetailsServiceImpl;
import top.wilsonlv.jaguar.security.component.AuthenticationExceptionHandler;
import top.wilsonlv.jaguar.security.component.JaguarAccessDeniedHandler;
import top.wilsonlv.jaguar.security.component.JaguarSessionExpiredStrategy;

/**
 * @author lvws
 * @since 2021/7/5
 */
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class AuthServerWebSecurityConfigurer extends WebSecurityConfigurerAdapter {

    public static final String LOGIN_PATH = "/login";

    public static final String LOGOUT_PATH = "/logout";

    private final PasswordEncoder passwordEncoder;

    private final UserDetailsServiceImpl userDetailsService;

    private final JaguarCaptchaFilter jaguarCaptchaFilter;

    private final LogoutLogFilter logoutLogFilter;

    private final LoginSuccessHandler loginSuccessHandler;

    private final LoginFailureHandler loginFailureHandler;

    private final LogoutSuccessHandler logoutSuccessHandler;

    private final JaguarAccessDeniedHandler jaguarAccessDeniedHandler;

    private final AuthenticationExceptionHandler authenticationExceptionHandler;

    private final ExtensionSecurityConfigurer extensionSecurityConfigurer;

    private final SpringSessionBackedSessionRegistry<?> redisSessionRegistry;

    @Override
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        ProviderManager providerManager = (ProviderManager) super.authenticationManager();

        //当spring容器中只有一个AuthenticationProvider的实现bean时，
        //super.authenticationManager()不会加载DaoAuthenticationProvider
        //需要手动配置

        boolean flag = false;
        for (AuthenticationProvider provider : providerManager.getProviders()) {
            if (provider.supports(UsernamePasswordAuthenticationToken.class)) {
                flag = true;
                break;
            }
        }

        if (!flag) {
            DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
            daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
            daoAuthenticationProvider.setUserDetailsService(userDetailsService);
            providerManager.getProviders().add(daoAuthenticationProvider);
        }
        return providerManager;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .apply(extensionSecurityConfigurer)
                .and()
                .sessionManagement()
                .maximumSessions(1)
                .sessionRegistry(redisSessionRegistry)
                .expiredSessionStrategy(new JaguarSessionExpiredStrategy())
                .and()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and()
                .addFilterBefore(jaguarCaptchaFilter, UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(logoutLogFilter, LogoutFilter.class)
                .formLogin()
                .loginProcessingUrl(LOGIN_PATH)
                .successHandler(loginSuccessHandler).failureHandler(loginFailureHandler)
                .and()
                .logout()
                .logoutUrl(LOGOUT_PATH)
                .logoutSuccessHandler(logoutSuccessHandler)
                .and()
                .authorizeRequests()
                .antMatchers(LOGIN_PATH + "/**", SmsApi.SMS_PATH + "/**").permitAll()
                .antMatchers("/oauth/token", "/oauth/token/logout", "/oauth/check_token").permitAll()
                .anyRequest().authenticated()
                .and().exceptionHandling()
                .accessDeniedHandler(jaguarAccessDeniedHandler)
                .authenticationEntryPoint(authenticationExceptionHandler)
                .and().cors()
                .and().csrf().disable()
                .headers().frameOptions().disable();
    }

}
